/**
 * @file xmlbase.cpp
 * @brief XML基类源文件
 * @author zhangpeihao
 */

#ifndef __WIN32__
#include <unistd.h>
#endif
#include <string>
#include <algorithm>
#include <cctype>

#include <zsystem/core/xml/xmlbase.h>

const std::string cc_true( "|true|1|yes|on|" ); ///< 真字符串定义
const std::string cc_false( "|false|0|no|off|" ); ///< 假字符串定义

#define CONFIG_PATH_SPLITTER  '.'

template<typename TYPE>
const Z_BOOL 
zsystem::xmlbase::parseSigned(const char* str, TYPE& value)
{
	if(str == Z_NULL)
		return Z_FALSE;
	Z_BOOL r = Z_TRUE;
	char* t = (char*)((Z_PTR_TYPE)str);
	TYPE sign = 1;
	if(*t == '-')
	{
		sign = -1;
		t++;
	}
	TYPE t1 = 0;
	TYPE t2 = 0;
	char b = 0;
	while(*t != '\0')
	{
		b = *((char*)t);
		if(b >= (char)'0' && b <= (char)'9')
		{
			t2 = t1 * 10 + (b - (char)'0') * sign;
			if(sign > 0)
			{
				if(t2 < t1)
				{
					// overflow
					r = Z_FALSE;
					break;
				}
				else
				{
					t1 = t2;
				}
			}
			else
			{
				if(t2 > t1)
				{
					// overflow
					r = Z_FALSE;
					break;
				}
				else
				{
					t1 = t2;
				}
			}
		}
		else if(b != (char)' ' && b != (char)'\t' && b != (char)'\n' && b != (char)'\r')
		{
			// invaliad charater
			r = Z_FALSE;
			break;
		}
		t++;
	}
	if(r == Z_TRUE)
	{
		value = t1;
	}
	return r;	
}

template<typename TYPE>
const Z_BOOL 
zsystem::xmlbase::parseUnsigned(const char* str, TYPE& value)
{
	if(str == Z_NULL)
		return Z_FALSE;
	Z_BOOL r = Z_TRUE;
	char* t = (char*)((Z_PTR_TYPE)str);
	TYPE t1 = 0;
	TYPE t2 = 0;
	char b = 0;
	while(*t != '\0')
	{
		b = *((char*)t);
		if(b >= (char)'0' && b <= (char)'9')
		{
			t2 = t1 * 10 + (b - (char)'0');
			if(t2 < t1)
			{
				// overflow
				r = Z_FALSE;
				break;
			}
			else
			{
				t1 = t2;
			}
		}
		else if(b != (char)' ' && b != (char)'\t' && b != (char)'\n' && b != (char)'\r')
		{
			// invaliad charater
			r = Z_FALSE;
			break;
		}
		t++;
	}
	if(r == Z_TRUE)
	{
		value = t1;
	}
	return r;	
}

template<typename TYPE>
const Z_BOOL 
zsystem::xmlbase::parseFloat(const char* str, TYPE& value)
{
	if(str == Z_NULL)
		return Z_FALSE;
	Z_BOOL r = Z_TRUE;
	char* t = (char*)((Z_PTR_TYPE)str);
	int sign = 1;
	if(*t == '-')
	{
		sign = -1;
		t++;
	}

	Z_U64 v = 0;
	Z_U64 dec_fector = 1;
	char b = 0;
	Z_BOOL dec = false;
	while(*t != '\0')
	{
		b = *((char*)t);
		if(b >= (char)'0' && b <= (char)'9')
		{
			if(dec)
			{
				dec_fector = dec_fector * 10;
			}
			v = v * 10 + (b - (char)'0');
		}
		else if(b == (char)'.')
		{
			if(dec)
			{
				// two dots
				r = Z_FALSE;
				break;
			}
			else
			{
				dec = true;
			}
		}
		else if(b != (char)' ' && b != (char)'\t' && b != (char)'\n' && b != (char)'\r')
		{
			// invaliad charater
			r = Z_FALSE;
			break;
		}
		t++;
	}
	if(r == Z_TRUE)
	{
		value = ((TYPE)v/(TYPE)dec_fector) * sign;
	}
	return r;	
}

const Z_BOOL 
zsystem::xmlbase::parseBoolean(const char* str, Z_BOOL& value)
{
	if(str == Z_NULL)
		return Z_FALSE;
	Z_BOOL r = Z_TRUE;

	std::string strv("|");
	strv = strv + str + "|";
	transform(strv.begin(), strv.end(), strv.begin(), ::tolower);
	if(cc_true.find(strv) != std::string::npos)
	{
		value = true;
	}
	else if(cc_false.find(strv) != std::string::npos)
	{
		value = false;
	}
	else
	{
		r = Z_FALSE;
	}
	return r;	
}


template const Z_BOOL zsystem::xmlbase::parseSigned<Z_I64>(const char* str, Z_I64& value);
template const Z_BOOL zsystem::xmlbase::parseSigned<Z_I32>(const char* str, Z_I32& value);
template const Z_BOOL zsystem::xmlbase::parseSigned<Z_I16>(const char* str, Z_I16& value);
template const Z_BOOL zsystem::xmlbase::parseSigned<Z_I8>(const char* str, Z_I8& value);

template const Z_BOOL zsystem::xmlbase::parseUnsigned<Z_U64>(const char* str, Z_U64& value);
template const Z_BOOL zsystem::xmlbase::parseUnsigned<Z_U32>(const char* str, Z_U32& value);
template const Z_BOOL zsystem::xmlbase::parseUnsigned<Z_U16>(const char* str, Z_U16& value);
template const Z_BOOL zsystem::xmlbase::parseUnsigned<Z_U8>(const char* str, Z_U8& value);

template const Z_BOOL zsystem::xmlbase::parseFloat<Z_FLOAT>(const char* str, Z_FLOAT& value);
template const Z_BOOL zsystem::xmlbase::parseFloat<Z_DOUBLE>(const char* str, Z_DOUBLE& value);
namespace zsystem{

#define PARSE_SPECIALIZATION_FUNC(TYPE, FUNC) \
  template<> const Z_BOOL xmlbase::parse<TYPE>(const char* str, TYPE& value){  return xmlbase::parse##FUNC<TYPE>(str, value);}

//
//template<>
//const Z_BOOL 
//xmlbase::parse<Z_U8>(const char* str, Z_U8& value){  return xmlbase::parseUnsigned<Z_U8>(str, value);}

PARSE_SPECIALIZATION_FUNC(Z_I8, Signed)
PARSE_SPECIALIZATION_FUNC(Z_I16, Signed)
PARSE_SPECIALIZATION_FUNC(Z_I32, Signed)
PARSE_SPECIALIZATION_FUNC(Z_U8, Unsigned)
PARSE_SPECIALIZATION_FUNC(Z_U16, Unsigned)
PARSE_SPECIALIZATION_FUNC(Z_U32, Unsigned)
PARSE_SPECIALIZATION_FUNC(Z_FLOAT, Float)
PARSE_SPECIALIZATION_FUNC(Z_DOUBLE, Float)

template<> const Z_BOOL xmlbase::parse<Z_BOOL>(const char* str, Z_BOOL& value){  return xmlbase::parseBoolean(str, value);}
#define INT64_STRING_LEN (10 * 2 + 1)
template<> const Z_BOOL xmlbase::parse<Z_I64>(const char* str, Z_I64& value)
{
	if (str == NULL)
		return Z_FALSE;
	char tmp[INT64_STRING_LEN + 1 + 1] = {0};
	Z_STRNCPY(tmp, str, sizeof(tmp));
	
	int sign = 1;
	char* pHigh = &(tmp[0]);
	if(tmp[0] == '-')
	{
		sign = -1;
		pHigh = &(tmp[1]);
	}
	char* pLow = pHigh + 1;

	while(pLow != NULL)
	{
		if(*pLow == '_')
		{
			*pLow = '\0';
			pLow++;
			break;
		}
		pLow++;
	}

	if (NULL == pLow || '\0' == *pLow)
	{
		return Z_FALSE;
	}

	Z_U32 high(0);
	if ( !parseUnsigned<Z_U32>(pHigh, high) || high > 0X7FFFFFFF )
	{
		return Z_FALSE;
	}
	Z_U32 low(0);
	if ( !parseUnsigned<Z_U32>(pLow, low) )
	{
		return Z_FALSE;
	}
	value = (Z_I64)((((Z_U64)high) << 32) | low);
	if (sign == -1)
	{
		value = -1LL * value;
	}
	return Z_TRUE;
}

template<> const Z_BOOL xmlbase::parse<Z_U64>(const char* str, Z_U64& value)
{
	if (str == NULL)
		return Z_FALSE;
	char tmp[INT64_STRING_LEN + 1] = {0};
	Z_STRNCPY(tmp, str, sizeof(tmp));
	char* pHigh = &(tmp[0]);
	char* pLow = pHigh + 1;
	while(pLow != NULL)
	{
		if(*pLow == '_')
		{
			*pLow = '\0';
			pLow++;
			break;
		}
		pLow++;
	}

	if (NULL == pLow || '\0' == *pLow)
	{
		return Z_FALSE;
	}

	Z_U32 high(0);
	if ( !parseUnsigned<Z_U32>(pHigh, high) )
	{
		return Z_FALSE;
	}
	Z_U32 low(0);
	if ( !parseUnsigned<Z_U32>(pLow, low) )
	{
		return Z_FALSE;
	}
	value = ((Z_U64)high << 32) | ((Z_U64)low);
	return Z_TRUE;
}



template<>
const Z_BOOL 
xmlbase::read<Z_U8>(const xmlNode node, Z_U8& value)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  TiXmlNode* textNode = Z_NULL;
  while( textNode = node->IterateChildren( textNode ) )
  {
    if (textNode->Type() == TiXmlNode::TINYXML_TEXT)
    {
      return xmlbase::parseUnsigned<Z_U8>(textNode->Value(), value);
    }
  }
  return Z_FALSE;
}

template<>
const Z_BOOL 
zsystem::xmlbase::read<Z_U16>(const xmlNode node, Z_U16& value)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  TiXmlNode* textNode = Z_NULL;
  while( textNode = node->IterateChildren( textNode ) )
  {
    if (textNode->Type() == TiXmlNode::TINYXML_TEXT)
    {
      return xmlbase::parseUnsigned<Z_U16>(textNode->Value(), value);
    }
  }
  return Z_FALSE;
}
template<>
const Z_BOOL 
zsystem::xmlbase::read<Z_U32>(const xmlNode node, Z_U32& value)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  TiXmlNode* textNode = Z_NULL;
  while( textNode = node->IterateChildren( textNode ) )
  {
    if (textNode->Type() == TiXmlNode::TINYXML_TEXT)
    {
      return xmlbase::parseUnsigned<Z_U32>(textNode->Value(), value);
    }
  }
  return Z_FALSE;
}
template<>
const Z_BOOL 
zsystem::xmlbase::read<Z_U64>(const xmlNode node, Z_U64& value)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  TiXmlNode* textNode = Z_NULL;
  while( textNode = node->IterateChildren( textNode ) )
  {
    if (textNode->Type() == TiXmlNode::TINYXML_TEXT)
    {
      return xmlbase::parseUnsigned<Z_U64>(textNode->Value(), value);
    }
  }
  return Z_FALSE;
}


template<>
const Z_BOOL 
zsystem::xmlbase::read<Z_I8>(const xmlNode node, Z_I8& value)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  TiXmlNode* textNode = Z_NULL;
  while( textNode = node->IterateChildren( textNode ) )
  {
    if (textNode->Type() == TiXmlNode::TINYXML_TEXT)
    {
      return xmlbase::parseSigned<Z_I8>(textNode->Value(), value);
    }
  }
  return Z_FALSE;
}

template<>
const Z_BOOL 
zsystem::xmlbase::read<Z_I16>(const xmlNode node, Z_I16& value)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  TiXmlNode* textNode = Z_NULL;
  while( textNode = node->IterateChildren( textNode ) )
  {
    if (textNode->Type() == TiXmlNode::TINYXML_TEXT)
    {
      return xmlbase::parseSigned<Z_I16>(textNode->Value(), value);
    }
  }
  return Z_FALSE;
}
template<>
const Z_BOOL 
zsystem::xmlbase::read<Z_I32>(const xmlNode node, Z_I32& value)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  TiXmlNode* textNode = Z_NULL;
  while( textNode = node->IterateChildren( textNode ) )
  {
    if (textNode->Type() == TiXmlNode::TINYXML_TEXT)
    {
      return xmlbase::parseSigned<Z_I32>(textNode->Value(), value);
    }
  }
  return Z_FALSE;
}
template<>
const Z_BOOL 
zsystem::xmlbase::read<Z_I64>(const xmlNode node, Z_I64& value)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  TiXmlNode* textNode = Z_NULL;
  while( textNode = node->IterateChildren( textNode ) )
  {
    if (textNode->Type() == TiXmlNode::TINYXML_TEXT)
    {
      return xmlbase::parseSigned<Z_I64>(textNode->Value(), value);
    }
  }
  return Z_FALSE;
}


template<>
const Z_BOOL 
zsystem::xmlbase::read<const char*>(const xmlNode node, const char*& value)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  TiXmlNode* textNode = Z_NULL;
  while( textNode = node->IterateChildren( textNode ) )
  {
    if (textNode->Type() == TiXmlNode::TINYXML_TEXT)
    {
      value = textNode->Value();
      return Z_TRUE;
    }
  }
  return Z_FALSE;
}


template<>
const Z_BOOL 
zsystem::xmlbase::read<std::string>(const xmlNode node, std::string& value)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  TiXmlNode* textNode = Z_NULL;
  while( textNode = node->IterateChildren( textNode ) )
  {
    if (textNode->Type() == TiXmlNode::TINYXML_TEXT)
    {
      value = textNode->Value();
      return Z_TRUE;
    }
  }
  return Z_FALSE;
}

template<>
const Z_BOOL 
zsystem::xmlbase::read<Z_BOOL>(const xmlNode node, Z_BOOL& value)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  TiXmlNode* textNode = Z_NULL;
  while( textNode = node->IterateChildren( textNode ) )
  {
    if (textNode->Type() == TiXmlNode::TINYXML_TEXT)
    {
      return xmlbase::parseBoolean(textNode->Value(), value);
    }
  }
  return Z_FALSE;
}

template<>
const Z_BOOL 
zsystem::xmlbase::readAttribute<Z_BOOL>(const xmlNode node, Z_BOOL& value, const char* attrName)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  const char* szValue = node->Attribute(attrName);
  if (Z_NULL == szValue)
  {
    return Z_FALSE;
  }
  return xmlbase::parseBoolean(szValue, value);
}

template<>
const Z_BOOL 
zsystem::xmlbase::readAttribute<Z_I8>(const xmlNode node, Z_I8& value, const char* attrName)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  const char* szValue = node->Attribute(attrName);
  if (Z_NULL == szValue)
  {
    return Z_FALSE;
  }
  return xmlbase::parseSigned<Z_I8>(szValue, value);
}
template<>
const Z_BOOL 
zsystem::xmlbase::readAttribute<Z_I16>(const xmlNode node, Z_I16& value, const char* attrName)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  const char* szValue = node->Attribute(attrName);
  if (Z_NULL == szValue)
  {
    return Z_FALSE;
  }
  return xmlbase::parseSigned<Z_I16>(szValue, value);
}
template<>
const Z_BOOL 
zsystem::xmlbase::readAttribute<Z_I32>(const xmlNode node, Z_I32& value, const char* attrName)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  const char* szValue = node->Attribute(attrName);
  if (Z_NULL == szValue)
  {
    return Z_FALSE;
  }
  return xmlbase::parseSigned<Z_I32>(szValue, value);
}
template<>
const Z_BOOL 
zsystem::xmlbase::readAttribute<Z_I64>(const xmlNode node, Z_I64& value, const char* attrName)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  const char* szValue = node->Attribute(attrName);
  if (Z_NULL == szValue)
  {
    return Z_FALSE;
  }
  return xmlbase::parseSigned<Z_I64>(szValue, value);
}

template<>
const Z_BOOL 
zsystem::xmlbase::readAttribute<Z_U8>(const xmlNode node, Z_U8& value, const char* attrName)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  const char* szValue = node->Attribute(attrName);
  if (Z_NULL == szValue)
  {
    return Z_FALSE;
  }
  return xmlbase::parseUnsigned<Z_U8>(szValue, value);
}
template<>
const Z_BOOL 
zsystem::xmlbase::readAttribute<Z_U16>(const xmlNode node, Z_U16& value, const char* attrName)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  const char* szValue = node->Attribute(attrName);
  if (Z_NULL == szValue)
  {
    return Z_FALSE;
  }
  return xmlbase::parseUnsigned<Z_U16>(szValue, value);
}
template<>
const Z_BOOL 
zsystem::xmlbase::readAttribute<Z_U32>(const xmlNode node, Z_U32& value, const char* attrName)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  const char* szValue = node->Attribute(attrName);
  if (Z_NULL == szValue)
  {
    return Z_FALSE;
  }
  return xmlbase::parseUnsigned<Z_U32>(szValue, value);
}
template<>
const Z_BOOL 
zsystem::xmlbase::readAttribute<Z_U64>(const xmlNode node, Z_U64& value, const char* attrName)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  const char* szValue = node->Attribute(attrName);
  if (Z_NULL == szValue)
  {
    return Z_FALSE;
  }
  return xmlbase::parseUnsigned<Z_U64>(szValue, value);
}


template<>
const Z_BOOL 
zsystem::xmlbase::readAttribute<const char*>(const xmlNode node, const char*& value, const char* attrName)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  const char* szValue = node->Attribute(attrName);
  if (Z_NULL == szValue)
  {
    return Z_FALSE;
  }
  value = szValue;
  return Z_TRUE;
}

template<>
const Z_BOOL 
zsystem::xmlbase::readAttribute<std::string>(const xmlNode node, std::string& value, const char* attrName)
{
  if (Z_NULL == node)
  {
    return Z_FALSE;
  }
  const char* szValue = node->Attribute(attrName);
  if (Z_NULL == szValue)
  {
    return Z_FALSE;
  }
  value = szValue;
  return Z_TRUE;
}

}


const Z_BOOL
zsystem::xmlbase::getChildNodeByPath(const xmlNode parentNode, const char* nodePath, xmlNode& childNode){
  if (Z_NULL == parentNode)
  {
    return Z_FALSE;
  }
  char nodeName[256] = {0};
  xmlNode node = parentNode;
  char tmp[1024] = {0};
  Z_STRNCPY(tmp, nodePath, sizeof(tmp));
  const char* path = nodePath;
  while(!nextNodeInPath(path, nodeName))
  {
    if (nodeName[0] == '\0')
    {
      return Z_FALSE;
    }
    node = node->FirstChildElement(nodeName);
    if (Z_NULL == node)
    {
      childNode = Z_NULL;
      return Z_FALSE;
    }
  }
  childNode = node->FirstChildElement(nodeName);
  return (childNode != Z_NULL);
}

zsystem::xmlNode 
zsystem::xmlbase::getChildNodeByPath(const xmlNode parentNode, const char* nodePath){
  if (Z_NULL == parentNode)
    return Z_NULL;
  char nodeName[256] = {0};
  xmlNode node = parentNode;
  char tmp[1024] = {0};
  Z_STRNCPY(tmp, nodePath, sizeof(tmp));
  const char* path = nodePath;
  while(!nextNodeInPath(path, nodeName))
  {
    if (nodeName[0] == '\0')
    {
      return Z_NULL;
    }
    node = node->FirstChildElement(nodeName);
    if (Z_NULL == node)
    {
      return Z_NULL;
    }
  }
  return node->FirstChildElement(nodeName);
}

const Z_BOOL
zsystem::xmlbase::nextNodeInPath(const char*& point, char* result)
{
  Z_BOOL finished(Z_TRUE);
  if (point == Z_NULL)
  {
    result[0] = '\0';
    return finished;
  }

  while (*point == CONFIG_PATH_SPLITTER)
  {
    point++;
    if (*point == '\0')
    {
      result[0] = '\0';
      return finished;
    }
  }

  const char* begin = point;
  Z_I32 len(0);
  finished = Z_FALSE;
  while(Z_TRUE)
  {
    if (*point == CONFIG_PATH_SPLITTER)
    {
      do
      {
        point++;
        if (*point == '\0')
        {
          finished = Z_TRUE;
          break;
        }
      } while (*point == CONFIG_PATH_SPLITTER);

      if (len == 0)
      {
        result[0] = '\0';
        finished = Z_TRUE;
      }
      else
      {
        // 取到节点名
        memcpy(result, begin, len);
        result[len] = '\0';
      }
      break;
    }
    else if (*point == '\0')
    {
      memcpy(result, begin, len);
      result[len] = '\0';
      finished = Z_TRUE;
      break;
    }
    point++;
    len++;
  }
  return finished;
}


const Z_BOOL 
zsystem::xmlbase::getNumber(const xmlNode parentNode, const char* nodeName, Z_U32& number)
{
    if (Z_NULL == parentNode)
      return Z_FALSE;
    xmlNode node = parentNode->FirstChildElement(nodeName);

    Z_U32 i = 0;
    for(; Z_NULL != node; i++)
    {
      node = node->NextSiblingElement(nodeName);
    }
    number = i;
    return Z_TRUE;
}

const Z_U32
zsystem::xmlbase::getNumber(const xmlNode parentNode, const char* nodeName)
{
    if (Z_NULL == parentNode)
      return 0;
    xmlNode node = parentNode->FirstChildElement(nodeName);

    Z_U32 i = 0;
    for(; Z_NULL != node; i++)
    {
      node = node->NextSiblingElement(nodeName);
    }
    return i;
}
